import json
from asyncio import iscoroutinefunction
from typing import Any, Union, Callable, Coroutine

from redis.asyncio import Redis, ConnectionPool

from app.core.config import settings
from app.core.log import logger


class RedisExt(Redis):
    """
    Redis扩展类
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def __getattr__(self, name):
        """
        将未定义的方法委托给原始的 Redis 客户端

        :param name: 方法名
        :return: 原始Redis客户端的方法
        """
        return getattr(self._redis_client, name)

    async def get_or_set(
        self,
        key: str,
        callable_func: Callable[[], Union[Any, Coroutine]],
        expire: int = None
    ) -> Any:
        """
        获取缓存值，如果不存在则执行回调函数并缓存结果

        :param key: 缓存键
        :param callable_func: 回调函数，当缓存不存在时调用，返回要设置的值
        :param expire: 过期时间(秒)，None表示不过期
        :return: 缓存值或回调函数返回值
        """
        # 尝试从缓存获取值
        value = await self.get(key)

        # 如果缓存存在，直接返回
        if value is not None:
            return json.loads(value)

        # 如果缓存不存在，执行回调函数
        if iscoroutinefunction(callable_func):
            result = await callable_func()
        else:
            result = callable_func()

        # 将结果缓存到redis
        result_str = json.dumps(result, ensure_ascii=False)
        if expire:
            await self.setex(key, expire, result_str)
        else:
            await self.set(key, result_str)

        return result_str


# 创建Redis连接池
_async_redis_pool = ConnectionPool.from_url(
    str(settings.redis_url),
    decode_responses=True,
    max_connections=settings.redis_max_connections,
)


class RedisConfiguration:
    """ Redis配置类，用于管理Redis客户端实例 """

    def __init__(self):
        _redis_client = None
        try:
            _redis_client = RedisExt(connection_pool=_async_redis_pool)
        except Exception as e:
            logger.exception("redis 连接失败", e)
            raise e
        self._redis_client = _redis_client

    def get_redis_client(self):
        """
        获取Redis客户端实例

        :return: Redis客户端实例
        """
        return self._redis_client


# 创建Redis配置实例
_redisConfig = RedisConfiguration()
# redis客户端 单例
redis_client = _redisConfig.get_redis_client()


async def connect_redis():
    """
    连接redis，验证连接状态

    :raises Exception: 当Redis连接失败时抛出异常
    """
    result = await redis_client.ping()
    if not result:
        raise Exception("redis 连接失败")
    logger.info(f"redis 连接成功: {result}")


async def disconnect_redis():
    """
    关闭redis连接，释放资源
    """
    await redis_client.aclose()
    logger.info("redis 断开连接成功")
